home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Oracle Corporation code.
- *
- * The Initial Developer of the Original Code is
- * Oracle Corporation
- * Portions created by the Initial Developer are Copyright (C) 2004
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
- * Mike Shaver <shaver@off.net>
- * Joey Minta <jminta@gmail.com>
- * Matthew Willis <lilmatt@mozilla.com>
- * Daniel Boelzle <daniel.boelzle@sun.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
- //
- // calItemBase.js
- //
-
- const ICAL = Components.interfaces.calIIcalComponent;
-
-
- function calItemBase() {
- ASSERT(false, "Inheriting objects call initItemBase!");
- }
-
- calItemBase.prototype = {
- mPropertyParams: null,
- mIsProxy: false,
-
- QueryInterface: function (aIID) {
- if (!aIID.equals(Components.interfaces.nsISupports) &&
- !aIID.equals(Components.interfaces.calIItemBase))
- {
- throw Components.results.NS_ERROR_NO_INTERFACE;
- }
-
- return this;
- },
-
- mHashId: null,
- get hashId() {
- if (this.mHashId === null) {
- var rid = this.recurrenceId;
- var cal = this.calendar;
- // some unused delim character:
- this.mHashId = [encodeURIComponent(this.id),
- rid ? rid.getInTimezone(UTC()).icalString : "",
- cal ? encodeURIComponent(cal.id) : ""].join("#");
- }
- return this.mHashId;
- },
-
- get id() {
- return this.getProperty("UID");
- },
- set id(uid) {
- this.modify();
- this.mHashId = null; // recompute hashId
- return this.setProperty("UID", uid);
- },
-
- get recurrenceId() {
- return this.getProperty("RECURRENCE-ID");
- },
- set recurrenceId(rid) {
- this.modify();
- this.mHashId = null; // recompute hashId
- return this.setProperty("RECURRENCE-ID", rid);
- },
-
- mParentItem: null,
- get parentItem() {
- if (this.mParentItem)
- return this.mParentItem;
- else
- return this;
- },
- set parentItem(value) {
- if (this.mImmutable)
- throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
- this.mIsProxy = true;
- this.mParentItem = value;
- },
-
- initializeProxy: function (aParentItem) {
- if (this.mImmutable)
- throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
-
- if (this.mParentItem != null)
- throw Components.results.NS_ERROR_FAILURE;
-
- this.mParentItem = aParentItem;
- this.mCalendar = aParentItem.mCalendar;
- this.mIsProxy = true;
- },
-
- //
- // calIItemBase
- //
- mImmutable: false,
- get isMutable() { return !this.mImmutable; },
-
- mDirty: false,
- modify: function() {
- if (this.mImmutable)
- throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
- this.mDirty = true;
- },
-
- ensureNotDirty: function() {
- if (!this.mDirty)
- return;
-
- if (this.mImmutable) {
- dump ("### Something tried to undirty a dirty immutable event!\n");
- throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
- }
-
- this.setProperty("LAST-MODIFIED", jsDateToDateTime(new Date()));
- this.mDirty = false;
- },
-
- makeItemBaseImmutable: function() {
- if (this.mImmutable)
- throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
-
- // make all our components immutable
- if (this.mRecurrenceInfo)
- this.mRecurrenceInfo.makeImmutable();
-
- if (this.mOrganizer)
- this.mOrganizer.makeImmutable();
- if (this.mAttendees) {
- for (var i = 0; i < this.mAttendees.length; i++)
- this.mAttendees[i].makeImmutable();
- }
-
- var e = this.mProperties.enumerator;
- while (e.hasMoreElements()) {
- var prop = e.getNext();
- var val = prop.value;
-
- if (prop.value instanceof Components.interfaces.calIDateTime) {
- if (prop.value.isMutable)
- prop.value.makeImmutable();
- }
- }
-
- if (this.alarmOffset) {
- this.alarmOffset.makeImmutable();
- if (this.alarmLastAck) {
- this.alarmLastAck.makeImmutable();
- }
- }
-
- this.ensureNotDirty();
- this.mImmutable = true;
- },
-
- hasSameIds: function(that) {
- return (that && this.id == that.id &&
- (this.recurrenceId == that.recurrenceId || // both null
- (this.recurrenceId && that.recurrenceId &&
- this.recurrenceId.compare(that.recurrenceId) == 0)));
- },
-
- // initialize this class's members
- initItemBase: function () {
- var now = jsDateToDateTime(new Date());
-
- this.mProperties = new calPropertyBag();
- this.mPropertyParams = {};
-
- this.setProperty("CREATED", now.clone());
- this.setProperty("LAST-MODIFIED", now.clone());
- this.setProperty("DTSTAMP", now);
-
- this.mAttendees = null;
-
- this.mRecurrenceInfo = null;
-
- this.mAttachments = null;
- },
-
- // for subclasses to use; copies the ItemBase's values
- // into m. aNewParent is optional
- cloneItemBaseInto: function (m, aNewParent) {
- this.ensureNotDirty();
-
- m.mImmutable = false;
- m.mIsProxy = this.mIsProxy;
- m.mParentItem = aNewParent || this.mParentItem;
- m.mHashId = this.mHashId;
- m.mCalendar = this.mCalendar;
- if (this.mRecurrenceInfo) {
- m.mRecurrenceInfo = this.mRecurrenceInfo.clone();
- m.mRecurrenceInfo.item = m;
- }
-
- if (this.mOrganizer) {
- m.mOrganizer = this.mOrganizer.clone();
- }
-
- if (this.mAttendees) {
- m.mAttendees = new Array(this.mAttendees.length);
- for (var i = 0; i < this.mAttendees.length; i++)
- m.mAttendees[i] = this.mAttendees[i].clone();
- }
- else
- m.mAttendees = null;
-
- m.mProperties = new calPropertyBag();
- var e = this.mProperties.enumerator;
- while (e.hasMoreElements()) {
- var prop = e.getNext();
- var name = prop.name;
- var val = prop.value;
-
- if (val instanceof Components.interfaces.calIDateTime) {
- val = val.clone();
- }
-
- m.mProperties.setProperty(name, val);
-
- var propBucket = this.mPropertyParams[name];
- if (propBucket) {
- var newBucket = {};
- for (var param in propBucket) {
- newBucket[param] = propBucket[param];
- }
- m.mPropertyParams[name] = newBucket;
- }
- }
-
- m.mDirty = false;
-
- // these need fixing
- m.mAttachments = this.mAttachments;
-
- // Clone any alarm info that exists, set it to null if it doesn't
- if (this.alarmOffset) {
- m.alarmOffset = this.alarmOffset.clone();
- if (this.alarmLastAck) {
- m.alarmLastAck = this.alarmLastAck.clone();
- } else {
- m.alarmLastAck = null;
- }
- } else {
- m.alarmOffset = null;
- }
- m.alarmRelated = this.alarmRelated;
-
- return m;
- },
-
- get lastModifiedTime() {
- this.ensureNotDirty();
- return this.getProperty("LAST-MODIFIED");
- },
-
- get stampTime() {
- var prop = this.getProperty("DTSTAMP");
- if (prop && prop.isValid)
- return prop;
- return this.getProperty("LAST-MODIFIED");
- },
-
- updateStampTime: function() {
- // can't update the stamp time on an immutable event
- if (this.mImmutable)
- return;
-
- this.modify();
- this.setProperty("DTSTAMP", jsDateToDateTime(new Date()));
- },
-
- get unproxiedPropertyEnumerator() {
- return this.mProperties.enumerator;
- },
-
- get propertyEnumerator() {
- if (this.mIsProxy) {
- // nsISimpleEnumerator sucks. It really, really sucks.
- // The interface is badly defined, it's not clear
- // what happens if you just keep calling getNext() without
- // calling hasMoreElements in between, which seems like more
- // of an informational thing. An interface with
- // "advance()" which returns true or false, and with "item()",
- // which returns the item the enumerator is pointing to, makes
- // far more sense. Right now we have getNext() doing both
- // item returning and enumerator advancing, which makes
- // no sense.
- return {
- firstEnumerator: this.mProperties.enumerator,
- secondEnumerator: this.mParentItem.propertyEnumerator,
- handledProperties: { },
-
- currentItem: null,
-
- QueryInterface: function(aIID) {
- if (!aIID.equals(Components.interfaces.nsISimpleEnumerator) ||
- !aIID.equals(Components.interfaces.nsISupports))
- {
- throw Components.results.NS_ERROR_NO_INTERFACE;
- }
- return this;
- },
-
- hasMoreElements: function() {
- if (!this.secondEnumerator)
- return false;
-
- if (this.firstEnumerator) {
- var moreFirst = this.firstEnumerator.hasMoreElements();
- if (moreFirst) {
- this.currentItem = this.firstEnumerator.getNext();
- this.handledProperties[this.currentItem.name] = true;
- return true;
- }
- this.firstEnumerator = null;
- }
-
- var moreSecond = this.secondEnumerator.hasMoreElements();
- if (moreSecond) {
- while (this.currentItem.name in this.handledProperties &&
- this.secondEnumerator.hasMoreElements())
- do {
- this.currentItem = this.secondEnumerator.getNext();
- } while (this.currentItem.name in this.handledProperties &&
- ((this.currentItem = null) == null) && // hack
- this.secondEnumerator.hasMoreElements());
-
- if (!this.currentItem)
- return false;
-
- return true;
- }
-
- this.secondEnumerator = null;
-
- return false;
- },
-
- getNext: function() {
- if (!this.currentItem)
- throw Components.results.NS_ERROR_UNEXPECTED;
-
- var rval = this.currentItem;
- this.currentItem = null;
- return rval;
- }
- };
- } else {
- return this.mProperties.enumerator;
- }
- },
-
- // The has/get/getUnproxied/set/deleteProperty methods are case-insensitive.
- getProperty: function (aName) {
- aName = aName.toUpperCase();
- var aValue = this.mProperties.getProperty(aName);
- if ((aValue === null) && this.mIsProxy) {
- aValue = this.mParentItem.getProperty(aName);
- }
- return aValue;
- },
-
- getUnproxiedProperty: function (aName) {
- return this.mProperties.getProperty(aName.toUpperCase());
- },
-
- hasProperty: function (aName) {
- return (this.getProperty(aName.toUpperCase()) != null);
- },
-
- setProperty: function (aName, aValue) {
- if (aName == "LAST-MODIFIED") {
- this.mDirty = false;
- } else {
- this.modify();
- }
- this.mProperties.setProperty(aName.toUpperCase(), aValue);
- },
-
- deleteProperty: function (aName) {
- this.modify();
- this.mProperties.deleteProperty(aName.toUpperCase());
- },
-
- getPropertyParameter: function getPP(aPropName, aParamName) {
- return this.mPropertyParams[aPropName][aParamName];
- },
-
- getAttendees: function (countObj) {
- if (!this.mAttendees && this.mIsProxy && this.mParentItem) {
- this.mAttendees = this.mParentItem.getAttendees(countObj);
- }
- if (this.mAttendees) {
- countObj.value = this.mAttendees.length;
- return this.mAttendees.concat([]); // clone
- }
- else {
- countObj.value = 0;
- return [];
- }
- },
-
- getAttendeeById: function (id) {
- var attendees = this.getAttendees({});
- var lowerCaseId = id.toLowerCase();
- for each (var attendee in attendees) {
- // This match must be case insensitive to deal with differing
- // cases of things like MAILTO:
- if (attendee.id.toLowerCase() == lowerCaseId) {
- return attendee;
- }
- }
- return null;
- },
-
- removeAttendee: function (attendee) {
- this.modify();
- var found = false, newAttendees = [];
- var attendees = this.getAttendees({});
- var attIdLowerCase =attendee.id.toLowerCase();
-
- for (var i = 0; i < attendees.length; i++) {
- if (attendees[i].id.toLowerCase() != attIdLowerCase)
- newAttendees.push(attendees[i]);
- else
- found = true;
- }
- if (found)
- this.mAttendees = newAttendees;
- else
- throw Component.results.NS_ERROR_INVALID_ARG;
- },
-
- removeAllAttendees: function() {
- this.modify();
- this.mAttendees = [];
- },
-
- addAttendee: function (attendee) {
- this.modify();
- this.mAttendees = this.getAttendees({});
- this.mAttendees.push(attendee);
- },
-
- mCalendar: null,
- get calendar () {
- return this.mCalendar;
- },
-
- set calendar (v) {
- if (this.mImmutable)
- throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
- this.mHashId = null; // recompute hashId
- this.mCalendar = v;
- },
-
- mOrganizer: null,
- get organizer() {
- if (!this.mOrganizer && this.mIsProxy && this.mParentItem) {
- return this.mParentItem.organizer;
- }
- else
- return this.mOrganizer;
- },
-
- set organizer(v) {
- this.modify();
- this.mOrganizer = v;
- },
-
- /* MEMBER_ATTR(mIcalString, "", icalString), */
- get icalString() {
- throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
- },
-
- set icalString() {
- throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
- },
-
- // All of these property names must be in upper case for isPropertyPromoted to
- // function correctly. The has/get/getUnproxied/set/deleteProperty interfaces
- // are case-insensitive, but these are not.
- itemBasePromotedProps: {
- "CREATED": true,
- "UID": true,
- "LAST-MODIFIED": true,
- "SUMMARY": true,
- "PRIORITY": true,
- "METHOD": true,
- "STATUS": true,
- "CLASS": true,
- "DTSTAMP": true,
- "RRULE": true,
- "EXDATE": true,
- "RDATE": true,
- "ATTENDEE": true,
- "ORGANIZER": true,
- "RECURRENCE-ID": true
- },
-
- icsBasePropMap: [
- { cal: "CREATED", ics: "createdTime" },
- { cal: "LAST-MODIFIED", ics: "lastModified" },
- { cal: "DTSTAMP", ics: "stampTime" },
- { cal: "UID", ics: "uid" },
- { cal: "SUMMARY", ics: "summary" },
- { cal: "PRIORITY", ics: "priority" },
- { cal: "STATUS", ics: "status" },
- { cal: "CLASS", ics: "icalClass" },
- { cal: "RECURRENCE-ID", ics: "recurrenceId" } ],
-
- mapPropsFromICS: function(icalcomp, propmap) {
- for (var i = 0; i < propmap.length; i++) {
- var prop = propmap[i];
- var val = icalcomp[prop.ics];
- if (val != null && val != ICAL.INVALID_VALUE)
- this.setProperty(prop.cal, val);
- }
- },
-
- mapPropsToICS: function(icalcomp, propmap) {
- for (var i = 0; i < propmap.length; i++) {
- var prop = propmap[i];
- var val = this.getProperty(prop.cal);
- if (val != null && val != ICAL.INVALID_VALUE)
- icalcomp[prop.ics] = val;
- }
- },
-
- setItemBaseFromICS: function (icalcomp) {
- this.modify();
-
- this.mapPropsFromICS(icalcomp, this.icsBasePropMap);
-
- for (var attprop = icalcomp.getFirstProperty("ATTENDEE");
- attprop;
- attprop = icalcomp.getNextProperty("ATTENDEE")) {
-
- var att = new CalAttendee();
- att.icalProperty = attprop;
- this.addAttendee(att);
- }
-
- var orgprop = icalcomp.getFirstProperty("ORGANIZER");
- if (orgprop) {
- var org = new CalAttendee();
- org.icalProperty = orgprop;
- org.isOrganizer = true;
- this.mOrganizer = org;
- }
-
- // find recurrence properties
- var rec = null;
- for (var recprop = icalcomp.getFirstProperty("ANY");
- recprop;
- recprop = icalcomp.getNextProperty("ANY"))
- {
- var ritem = null;
- if (recprop.propertyName == "RRULE" ||
- recprop.propertyName == "EXRULE")
- {
- ritem = new CalRecurrenceRule();
- } else if (recprop.propertyName == "RDATE" ||
- recprop.propertyName == "EXDATE")
- {
- ritem = new CalRecurrenceDate();
- } else {
- continue;
- }
-
- ritem.icalProperty = recprop;
-
- if (!rec) {
- rec = new CalRecurrenceInfo();
- rec.item = this;
- }
-
- rec.appendRecurrenceItem(ritem);
- }
- this.mRecurrenceInfo = rec;
-
- var alarmComp = icalcomp.getFirstSubcomponent("VALARM");
- if (alarmComp) {
- var triggerProp = alarmComp.getFirstProperty("TRIGGER");
- // Really, really old Sunbird/Calendar versions didn't give us a
- // trigger.
- if (!triggerProp) {
- Components.utils.reportError("No trigger property for alarm on item: "+this.id);
- // No parsing happens after alarms, so just return
- return;
- }
- var duration = Components.classes["@mozilla.org/calendar/duration;1"]
- .createInstance(Components.interfaces.calIDuration);
- duration.icalString = triggerProp.valueAsIcalString;
- this.alarmOffset = duration;
-
- var related = triggerProp.getParameter("RELATED");
- if (related && related == "END")
- this.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_END;
- else
- this.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_START;
-
- var lastAck = alarmComp.getFirstProperty("X-MOZ-LASTACK");
- if (lastAck) {
- var lastAckTime = Components.classes["@mozilla.org/calendar/datetime;1"]
- .createInstance(Components.interfaces.calIDateTime);
- lastAckTime.icalString = lastAck.valueAsIcalString;
- this.alarmLastAck = lastAckTime;
- }
-
- var email = alarmComp.getFirstProperty("X-EMAILADDRESS");
- if (email)
- this.setProperty("alarmEmailAddress", email.value);
- }
- },
-
- importUnpromotedProperties: function (icalcomp, promoted) {
- for (var prop = icalcomp.getFirstProperty("ANY");
- prop;
- prop = icalcomp.getNextProperty("ANY")) {
- if (!promoted[prop.propertyName]) {
- this.setProperty(prop.propertyName, prop.value);
- var param = prop.getFirstParameterName();
- while (param) {
- if (!(prop.propertyName in this.mPropertyParams)) {
- this.mPropertyParams[prop.propertyName] = {};
- }
- this.mPropertyParams[prop.propertyName][param] = prop.getParameter(param);
- param = prop.getNextParameterName();
- }
- }
- }
- },
-
- // This method is case-insensitive.
- isPropertyPromoted: function (name) {
- return (this.itemBasePromotedProps[name.toUpperCase()]);
- },
-
- get icalComponent() {
- throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
- },
-
- get generation() {
- if (this.mGeneration === undefined) {
- var gen = this.getProperty("X-MOZ-GENERATION");
- this.mGeneration = (gen ? parseInt(gen) : 0);
- }
- return this.mGeneration;
- },
- set generation(aValue) {
- this.modify();
- this.mGeneration = aValue;
- this.setProperty("X-MOZ-GENERATION", String(aValue));
- return aValue;
- },
-
- fillIcalComponentFromBase: function (icalcomp) {
- // Make sure that the LMT and ST are updated
- this.updateStampTime();
- this.ensureNotDirty();
-
- this.mapPropsToICS(icalcomp, this.icsBasePropMap);
-
- if (this.mOrganizer)
- icalcomp.addProperty(this.mOrganizer.icalProperty);
- var attendees = this.getAttendees({});
- if (attendees.length > 0) {
- for (var i = 0; i < attendees.length; i++) {
- icalcomp.addProperty(attendees[i].icalProperty);
- }
- }
-
- if (this.mRecurrenceInfo) {
- var ritems = this.mRecurrenceInfo.getRecurrenceItems({});
- for (i in ritems) {
- icalcomp.addProperty(ritems[i].icalProperty);
- }
- }
-
- if (this.alarmOffset) {
- var icssvc = getIcsService();
- var alarmComp = icssvc.createIcalComponent("VALARM");
-
- var triggerProp = icssvc.createIcalProperty("TRIGGER");
- triggerProp.valueAsIcalString = this.alarmOffset.icalString;
-
- if (this.alarmRelated == Components.interfaces.calIItemBase.ALARM_RELATED_END)
- triggerProp.setParameter("RELATED", "END");
-
- alarmComp.addProperty(triggerProp);
-
- if (this.alarmLastAck) {
- var lastAck = icssvc.createIcalProperty("X-MOZ-LASTACK");
- lastAck.valueAsIcalString = this.alarmLastAck.icalString;
- alarmComp.addProperty(lastAck);
- }
-
- // We don't use this, but the ics-spec requires it
- var descProp = icssvc.createIcalProperty("DESCRIPTION");
- descProp.value = "Mozilla Alarm: "+ this.title;
- alarmComp.addProperty(descProp);
-
- var actionProp = icssvc.createIcalProperty("ACTION");
- actionProp.value = "DISPLAY";
-
- if (this.getProperty("alarmEmailAddress")) {
- var emailProp = icssvc.createIcalProperty("X-EMAILADDRESS");
- emailProp.value = this.getProperty("alarmEmailAddress");
- actionProp.value = "EMAIL";
- alarmComp.addProperty(emailProp);
- }
-
- alarmComp.addProperty(actionProp);
-
- icalcomp.addSubcomponent(alarmComp);
- }
- },
-
- getOccurrencesBetween: function(aStartDate, aEndDate, aCount) {
- throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
- }
- };
-
- makeMemberAttr(calItemBase, "CREATED", null, "creationDate", true);
- makeMemberAttr(calItemBase, "SUMMARY", null, "title", true);
- makeMemberAttr(calItemBase, "PRIORITY", 0, "priority", true);
- makeMemberAttr(calItemBase, "CLASS", "PUBLIC", "privacy", true);
- makeMemberAttr(calItemBase, "STATUS", null, "status", true);
- makeMemberAttr(calItemBase, "ALARMTIME", null, "alarmTime", true);
-
- makeMemberAttr(calItemBase, "mRecurrenceInfo", null, "recurrenceInfo");
- makeMemberAttr(calItemBase, "mAttachments", null, "attachments");
- makeMemberAttr(calItemBase, "mProperties", null, "properties");
-
- function makeMemberAttr(ctor, varname, dflt, attr, asProperty)
- {
- // XXX handle defaults!
- var getter = function () {
- if (asProperty)
- return this.getProperty(varname);
- else
- return this[varname];
- };
- var setter = function (v) {
- this.modify();
- if (asProperty)
- return this.setProperty(varname, v);
- else
- return (this[varname] = v);
- };
- ctor.prototype.__defineGetter__(attr, getter);
- ctor.prototype.__defineSetter__(attr, setter);
- }
-
- //
- // helper functions
- //
-
- function icalFromString(str)
- {
- return getIcsService().parseICS(str, null);
- }
-
- function icalProp(kind)
- {
- return getIcsService().createIcalProperty(kind);
- }
-